iT邦幫忙

2024 iThome 鐵人賽

DAY 23
0
Security

picoCTF系列 第 23

[Day 23] weirdSnake

  • 分享至 

  • xImage
  •  

看到題目和提示,知道我們解題的方法和分析 bytecode 有關。
https://ithelp.ithome.com.tw/upload/images/20240828/20168342CK79gsvoOb.png
hint 1:Download and try to reverse the python bytecode.
hint 2:https://docs.python.org/3/library/dis.html

將題目給的檔案下載下來,得到一個文字檔。

$ ls -l
-rw-rw-r-- 1     user      user      6039 Mar 12 00:36 snake
$ file snake          
snake: ASCII text

使用 cat 檢視檔案內文,可以發現內文很像 symbol table,我們一一來看每個區塊代表的意思,嘗試還原檔案。

	 1          0 LOAD_CONST               0 (4)
              2 LOAD_CONST               1 (54)
              4 LOAD_CONST               2 (41)
              6 LOAD_CONST               3 (0)
              8 LOAD_CONST               4 (112)
             10 LOAD_CONST               5 (32)
             12 LOAD_CONST               6 (25)
             14 LOAD_CONST               7 (49)
             16 LOAD_CONST               8 (33)
             18 LOAD_CONST               9 (3)
             20 LOAD_CONST               3 (0)
             22 LOAD_CONST               3 (0)
             24 LOAD_CONST              10 (57)
             26 LOAD_CONST               5 (32)
             28 LOAD_CONST              11 (108)
             30 LOAD_CONST              12 (23)
             32 LOAD_CONST              13 (48)
             34 LOAD_CONST               0 (4)
             36 LOAD_CONST              14 (9)
             38 LOAD_CONST              15 (70)
             40 LOAD_CONST              16 (7)
             42 LOAD_CONST              17 (110)
             44 LOAD_CONST              18 (36)
             46 LOAD_CONST              19 (8)
             48 LOAD_CONST              11 (108)
             50 LOAD_CONST              16 (7)
             52 LOAD_CONST               7 (49)
             54 LOAD_CONST              20 (10)
             56 LOAD_CONST               0 (4)
             58 LOAD_CONST              21 (86)
             60 LOAD_CONST              22 (43)
             62 LOAD_CONST              23 (104)
             64 LOAD_CONST              24 (44)
             66 LOAD_CONST              25 (91)
             68 LOAD_CONST              26 (7)
             70 LOAD_CONST              27 (18)
             72 LOAD_CONST              28 (106)
             74 LOAD_CONST              29 (124)
             76 LOAD_CONST              20 (89)
             78 LOAD_CONST              30 (78)
             80 BUILD_LIST              40
             82 STORE_NAME               0 (input_list)

  2          84 LOAD_CONST              31 ('J')
             86 STORE_NAME               1 (key_str)

  3          88 LOAD_CONST              32 ('_')
             90 LOAD_NAME                1 (key_str)
             92 BINARY_ADD
             94 STORE_NAME               1 (key_str)

  4          96 LOAD_NAME                1 (key_str)
             98 LOAD_CONST              33 ('o')
            100 BINARY_ADD
            102 STORE_NAME               1 (key_str)

  5         104 LOAD_NAME                1 (key_str)
            106 LOAD_CONST              34 ('3')
            108 BINARY_ADD
            110 STORE_NAME               1 (key_str)

  6         112 LOAD_CONST              35 ('t')
            114 LOAD_NAME                1 (key_str)
            116 BINARY_ADD
            118 STORE_NAME               1 (key_str)

  9         120 LOAD_CONST              36 (<code object <listcomp> at 0x7f704e8a4d40, file "snake.py", line 9>)
            122 LOAD_CONST              37 ('<listcomp>')
            124 MAKE_FUNCTION            0
            126 LOAD_NAME                1 (key_str)
            128 GET_ITER
            130 CALL_FUNCTION            1
            132 STORE_NAME               2 (key_list)

 11     >>  134 LOAD_NAME                3 (len)
            136 LOAD_NAME                2 (key_list)
            138 CALL_FUNCTION            1
            140 LOAD_NAME                3 (len)
            142 LOAD_NAME                0 (input_list)
            144 CALL_FUNCTION            1
            146 COMPARE_OP               0 (<)
            148 POP_JUMP_IF_FALSE      162

 12         150 LOAD_NAME                2 (key_list)
            152 LOAD_METHOD              4 (extend)
            154 LOAD_NAME                2 (key_list)
            156 CALL_METHOD              1
            158 POP_TOP
            160 JUMP_ABSOLUTE          134

 15     >>  162 LOAD_CONST              38 (<code object <listcomp> at 0x7f704e8a4df0, file "snake.py", line 15>)
            164 LOAD_CONST              37 ('<listcomp>')
            166 MAKE_FUNCTION            0
            168 LOAD_NAME                5 (zip)
            170 LOAD_NAME                0 (input_list)
            172 LOAD_NAME                2 (key_list)
            174 CALL_FUNCTION            2
            176 GET_ITER
            178 CALL_FUNCTION            1
            180 STORE_NAME               6 (result)

 18         182 LOAD_CONST              39 ('')
            184 LOAD_METHOD              7 (join)
            186 LOAD_NAME                8 (map)
            188 LOAD_NAME                9 (chr)
            190 LOAD_NAME                6 (result)
            192 CALL_FUNCTION            2
            194 CALL_METHOD              1
            196 STORE_NAME              10 (result_text)
            198 LOAD_CONST              40 (None)
            200 RETURN_VALUE

Disassembly of <code object <listcomp> at 0x7f704e8a4d40, file "snake.py", line 9>:
  9           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                12 (to 18)
              6 STORE_FAST               1 (char)
              8 LOAD_GLOBAL              0 (ord)
             10 LOAD_FAST                1 (char)
             12 CALL_FUNCTION            1
             14 LIST_APPEND              2
             16 JUMP_ABSOLUTE            4
        >>   18 RETURN_VALUE

Disassembly of <code object <listcomp> at 0x7f704e8a4df0, file "snake.py", line 15>:
 15           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                16 (to 22)
              6 UNPACK_SEQUENCE          2
              8 STORE_FAST               1 (a)
             10 STORE_FAST               2 (b)
             12 LOAD_FAST                1 (a)
             14 LOAD_FAST                2 (b)
             16 BINARY_XOR
             18 LIST_APPEND              2
             20 JUMP_ABSOLUTE            4
        >>   22 RETURN_VALUE

看到第一個區塊,會發現 load_const 出現了很多次,第82行出現 store_name 。這裡的意思是將數字載入 register,並且最後將所有載入的數字儲存在名為 input_list 的陣列裡。例如第一行,便是將數字 4 存入 0 號 register。

	 1          0 LOAD_CONST               0 (4)
              2 LOAD_CONST               1 (54)
              4 LOAD_CONST               2 (41)
              6 LOAD_CONST               3 (0)
              8 LOAD_CONST               4 (112)
             10 LOAD_CONST               5 (32)
             12 LOAD_CONST               6 (25)
             14 LOAD_CONST               7 (49)
             16 LOAD_CONST               8 (33)
             18 LOAD_CONST               9 (3)
             20 LOAD_CONST               3 (0)
             22 LOAD_CONST               3 (0)
             24 LOAD_CONST              10 (57)
             26 LOAD_CONST               5 (32)
             28 LOAD_CONST              11 (108)
             30 LOAD_CONST              12 (23)
             32 LOAD_CONST              13 (48)
             34 LOAD_CONST               0 (4)
             36 LOAD_CONST              14 (9)
             38 LOAD_CONST              15 (70)
             40 LOAD_CONST              16 (7)
             42 LOAD_CONST              17 (110)
             44 LOAD_CONST              18 (36)
             46 LOAD_CONST              19 (8)
             48 LOAD_CONST              11 (108)
             50 LOAD_CONST              16 (7)
             52 LOAD_CONST               7 (49)
             54 LOAD_CONST              20 (10)
             56 LOAD_CONST               0 (4)
             58 LOAD_CONST              21 (86)
             60 LOAD_CONST              22 (43)
             62 LOAD_CONST              23 (104)
             64 LOAD_CONST              24 (44)
             66 LOAD_CONST              25 (91)
             68 LOAD_CONST              26 (7)
             70 LOAD_CONST              27 (18)
             72 LOAD_CONST              28 (106)
             74 LOAD_CONST              29 (124)
             76 LOAD_CONST              20 (89)
             78 LOAD_CONST              30 (78)
             80 BUILD_LIST              40
             82 STORE_NAME               0 (input_list)

依序填入數字,便能得到 input_list 。

input_list = [4, 54, 41, 0, 112, 32, 25, 49, 33, 3, 0, 0, 57, 32, 108, 23, 48, 4, 9, 70, 7, 110, 36, 8, 108, 7, 49, 10, 4, 86, 43, 104, 44, 91, 7, 18, 106, 124, 89, 78]

接著看到 區塊 2 到 區塊 6。

看到 區塊 2 中先載入了 ‘J’,再將 J 儲存至 key_str 中。( 目前 key_str = ‘J’ )
看到 區塊 3,先載入了 '',再載入 key_str,也就是 J,然後將 ‘’ 和 key_str 相加,也就是 '_' + 'J',得到 '_J’ 後,再儲存至 key_str。( 目前 key_str = ‘_J‘ )
看到 區塊 4,先載入了 key_str ( ‘_J‘ ),並且將 key_str 和 o 相加,也就是 '_J’ + ‘o',得到 '_Jo',並且儲存進入 key_str。( 目前 key_str = ‘_Jo‘ )
看到 區塊 5,先載入了 key_str ( ‘_Jo‘ ),並且將 key_str 和 3 相加,也就是 '_Jo’ + ‘3',得到 '_Jo3',並且儲存進入 key_str。( 目前 key_str = ‘_Jo3‘ )
看到 區塊 6,先載入了 ’t’,再載入 key_str ( ‘_Jo3‘ ),並且將 ‘t’ 和 key_str 相加,也就是 ‘t’ + ‘_Jo3‘,得到 't_Jo3',並且儲存進入 key_str,得到 key_str = ‘t_Jo3‘ 。

  2          84 LOAD_CONST              31 ('J')
             86 STORE_NAME               1 (key_str)

  3          88 LOAD_CONST              32 ('_')
             90 LOAD_NAME                1 (key_str)
             92 BINARY_ADD
             94 STORE_NAME               1 (key_str)

  4          96 LOAD_NAME                1 (key_str)
             98 LOAD_CONST              33 ('o')
            100 BINARY_ADD
            102 STORE_NAME               1 (key_str)

  5         104 LOAD_NAME                1 (key_str)
            106 LOAD_CONST              34 ('3')
            108 BINARY_ADD
            110 STORE_NAME               1 (key_str)

  6         112 LOAD_CONST              35 ('t')
            114 LOAD_NAME                1 (key_str)
            116 BINARY_ADD
            118 STORE_NAME               1 (key_str)
key_str = 't_Jo3'

再來看到 區塊 9,會發現呼叫了 0x7ff3b9776d40 這個位置的函式,並且 key_str 會作為其的參數,並且這個 code 回傳的結果會被儲存在 key_list 中。

  9         120 LOAD_CONST              36 (<code object <listcomp> at 0x7f704e8a4d40, file "snake.py", line 9>)
            122 LOAD_CONST              37 ('<listcomp>')
            124 MAKE_FUNCTION            0
            126 LOAD_NAME                1 (key_str)
            128 GET_ITER
            130 CALL_FUNCTION            1
            132 STORE_NAME               2 (key_list)

於是看到 0x7ff3b9776d40 這個位置的函式,從第4行可以看到這是一個有關 for 的 code。第 6 行能夠看到從 區塊 9 傳遞的參數 ( key_str ) 會被一個字元一個字元逐一讀取,並且會儲存 ord (char),所以能得當 key_list = [ ord(char) for char in key_str ]。

Disassembly of <code object <listcomp> at 0x7f704e8a4d40, file "snake.py", line 9>:
  9           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                12 (to 18)
              6 STORE_FAST               1 (char)
              8 LOAD_GLOBAL              0 (ord)
             10 LOAD_FAST                1 (char)
             12 CALL_FUNCTION            1
             14 LIST_APPEND              2
             16 JUMP_ABSOLUTE            4
        >>   18 RETURN_VALUE

key_list = [ord(char) for char in key_str]

緊接著看到 區塊 11、12,從 區塊 11 中第 148 行看到當 len(key_list) < len(input_list) == false 時才會 pop 至第 162 行,而當 len(key_list) < len(input_list) ==True,會載入 key_list,並且 extend key_list,並且再回到 134 行,所以我們能夠得到一個 while loop。

 11     >>  134 LOAD_NAME                3 (len)
            136 LOAD_NAME                2 (key_list)
            138 CALL_FUNCTION            1
            140 LOAD_NAME                3 (len)
            142 LOAD_NAME                0 (input_list)
            144 CALL_FUNCTION            1
            146 COMPARE_OP               0 (<)
            148 POP_JUMP_IF_FALSE      162

 12         150 LOAD_NAME                2 (key_list)
            152 LOAD_METHOD              4 (extend)
            154 LOAD_NAME                2 (key_list)
            156 CALL_METHOD              1
            158 POP_TOP
            160 JUMP_ABSOLUTE          134
while len(key_list) < len(input_list): 
    key_list.extend(key_list)

再來看到 區塊 15 ,首先會先呼叫 0x7ff3b9776df0 這個位置的函式,並且將 zip ( input_list, key_list ) 傳入,最後再將函式儲存的值命名為 result。

 15     >>  162 LOAD_CONST              38 (<code object <listcomp> at 0x7f704e8a4df0, file "snake.py", line 15>)
            164 LOAD_CONST              37 ('<listcomp>')
            166 MAKE_FUNCTION            0
            168 LOAD_NAME                5 (zip)
            170 LOAD_NAME                0 (input_list)
            172 LOAD_NAME                2 (key_list)
            174 CALL_FUNCTION            2
            176 GET_ITER
            178 CALL_FUNCTION            1
            180 STORE_NAME               6 (result)

於是我們看到 0x7ff3b9776df0 這個位置的函式,第 12 至 16 行可以看到,在每個 iteration 中,會載入 a b 這兩個變數,而這兩個變數是由 sector 15 傳入的 zip ( input_list, key_list ) 提供,再看到第 8、10、16 行,可以看到會將 a xor b 儲存進入 register。

Disassembly of <code object <listcomp> at 0x7f704e8a4df0, file "snake.py", line 15>:
 15           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                16 (to 22)
              6 UNPACK_SEQUENCE          2
              8 STORE_FAST               1 (a)
             10 STORE_FAST               2 (b)
             12 LOAD_FAST                1 (a)
             14 LOAD_FAST                2 (b)
             16 BINARY_XOR
             18 LIST_APPEND              2
             20 JUMP_ABSOLUTE            4
        >>   22 RETURN_VALUE

於是可以得到 result。

result = [a ^ b for a, b in zip(input_list, key_list)]

最後再看到 區塊 18,第 192 行 和 194 行可以知道,會先呼叫 map 這個 function,再呼叫 .join 這個 method,再把最後結果儲存在 result_text 裡。

 18         182 LOAD_CONST              39 ('')
            184 LOAD_METHOD              7 (join)
            186 LOAD_NAME                8 (map)
            188 LOAD_NAME                9 (chr)
            190 LOAD_NAME                6 (result)
            192 CALL_FUNCTION            2
            194 CALL_METHOD              1
            196 STORE_NAME              10 (result_text)
            198 LOAD_CONST              40 (None)
            200 RETURN_VALUE

結果如以下。

result_text = ''.join(map(chr, result))

最後完整個程式碼如下。

#!/usr/bin/env python3

input_list = [4, 54, 41, 0, 112, 32, 25, 49, 33, 3, 0, 0, 57, 32, 108, 23, 48, 4, 9, 70, 7, 110, 36, 8, 108, 7, 49, 10, 4, 86, 43, 104, 44, 91, 7, 18, 106, 124, 89, 78]

key_str = 't_Jo3'

key_list = [ord(char) for char in key_str]

while len(key_list) < len(input_list): 
    key_list.extend(key_list)

result = [a ^ b for a, b in zip(input_list, key_list)]
result_text = ''.join(map(chr, result))

print(result_text)

執行檔案,可以得到 flag。

$ python3 script.py 
picoCTF{N0t_sO_coNfus1ng_sn@ke_7f44f566}

小結:
學習分析 bytecode。


上一篇
[Day 22] dont-you-love-banners
下一篇
[Day 24] VNE
系列文
picoCTF30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言